/*
 * $Id: IPTDSSocketHandler.java,v 1.7 2009/10/22 22:25:23 afaichun Exp $
 * 
 * Copyright (C) 2008 General Electric Company. All Rights Reserved.
 * 
 * This software is the confidential and proprietary information of the General
 * Electric Company (GE). You shall not disclose this software and shall use it
 * only in accordance with the terms of the license agreement you entered into
 * with GE.
 * 
 * GE MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT. GE SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING, OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES.
 */
package com.ge.healthcare.autosc.processor.iptds;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.List;
import java.util.Vector;

import com.ge.healthcare.autosc.common.ASCLogger;
import com.ge.healthcare.autosc.common.util.ASCIOHandler;
import com.ge.healthcare.autosc.processor.exception.IPTDSException;
import com.ge.healthcare.autosc.processor.exception.IPTDSSocketException;
import com.ge.healthcare.autosc.processor.iip.comm.ClientDrvCommand;
/**
 * This class is not thread-safe, meaning it will produce unexpected result
 * under multi-threaded environment.
 * @author 212042946
 *
 */
public class IPTDSSocketHandler {

	private Socket socket = null;
	private OutputStream outputStream = null;
	private InputStream inputStream = null;
	private BufferedReader input = null;
	private PrintStream output = null;
	
	/**
	 * Constructor
	 * @param socket
	 * @throws IPTDSException
	 */
	public IPTDSSocketHandler(Socket socket) 
		throws IPTDSException {
		if(socket == null) {
			throw new IPTDSException("Socket object is required to create IPTDSSocketHandler.");
		}
		this.socket = socket;
	}

	/**
	 * Open input and output socket stream
	 * @throws IPTDSException
	 */
	public void open() throws IPTDSException {
		try {
			outputStream = socket.getOutputStream();
			inputStream = socket.getInputStream();
			output = new PrintStream(outputStream);
			input = new BufferedReader(new InputStreamReader(inputStream));
		} catch (IOException ioe) {
			String msg = "Error opening socket stream";
			ASCLogger.error(this.getClass(), "open", msg, new IPTDSSocketException(ioe));
			throw new IPTDSException(msg, ioe);
		}
	}
	
	/**
	 * Close socket including opening streams.
	 */
	public void close() {
		String methodName = "close";
		String msg = new StringBuffer()
			.append("Local address: ").append(socket.getLocalAddress().toString()).append("\n")
			.append("local port: ").append(Integer.valueOf(socket.getLocalPort())).toString();
		try {
			if(input != null) {
				input.close();
			}
		} catch (IOException ioe) {
			ASCLogger.warn(this.getClass(), methodName,
					"Failed closing socket input stream\n" + msg, ioe);	
		}
		
		if(output != null) {
			output.close();
		}
		
		try {
			if((socket != null) && (!socket.isClosed())) {
				socket.close();
			}
		} catch (IOException ioe) {
			ASCLogger.warn(this.getClass(), methodName,
					"Failed closing socket\n" + msg, ioe);
		}
	}
	
	/**
	 * Read line of message from input stream.
	 * @return read message
	 * @throws IPTDSException
	 */
	public String readLine() throws IPTDSException {
		String line = null;
		if(input != null) {
			try {
				line = ASCIOHandler.readSingleLine(input);
				if(line != null) {
					line = line.trim();
					ASCLogger.debug(this.getClass(), "readLine", "Read: " + line);
				}
			} catch (IOException ioe) {
				String msg = "Error reading data from socket input.";
				ASCLogger.error(this.getClass(), "readLine", msg, new IPTDSSocketException(ioe));
				throw new IPTDSException(msg, ioe);
			}
		}
		return line;
	}
	
	/**
	 * Send message to output stream and then read from the
	 * input stream.
	 * @param message
	 * @return
	 * @throws IPTDSException
	 */
	public String readReplyFor(String message) 
		throws IPTDSException {
		String reply = null;
		if(sendMessage(message) > 0) {
			reply = readLine();
		}
		return reply;
	}
	
	/**
	 * Send message to output stream and read given number of reply
	 * from input stream.
	 * @param message	message to send
	 * @param numOfReply	number of expected reply
	 * @return	List of reply
	 * @throws IPTDSException
	 */
	public List<String> readReplyFor(String message, int numOfReply) 
		throws IPTDSException {
		Vector<String> replies = new Vector<String>();
		if(sendMessage(message) > 0) {
			try {
				for(int i=0; i<numOfReply; i++) {
					String reply = readLine();
					if(reply != null) {
						replies.add(reply);
					} 
				}
			} catch (IPTDSException ispe) {
				String msg = "Failed reading response from socket";
				ASCLogger.error(this.getClass(), "readReplyFor", msg);
				throw ispe;
			}
		} 
		return replies;
	}
	
	/**
	 * Send message to output stream
	 * @param message
	 * @return
	 */
	public int sendMessage(String message) {
		int writeNum = 0;
		if(message != null) {
			ASCLogger.debug(this.getClass(), "sendMessage", "Write: " + message);
			PrintStream toTarget = null;
			toTarget = new PrintStream(output, true);
			toTarget.println(message);
			toTarget.flush();
			writeNum = message.length();
		}
		return writeNum;
	}
	
	/**
	 * Use the defined socket stream to create clientDrvCommand
	 * @return ClientDrvCommand
	 */
	public ClientDrvCommand moveConnectionToClientDrv() {
		ClientDrvCommand clientDrv = null;
		if((inputStream != null) && (outputStream != null)) {
			clientDrv = new ClientDrvCommand();
			clientDrv.setInputStream(new DataInputStream(inputStream));
			clientDrv.setOutputStream(new DataOutputStream(outputStream));
		}
		return clientDrv;
	}
	
	/**
	 * Get host address
	 * @return host IP address
	 */
	public String getHostAddress() {
		return socket.getInetAddress().getHostAddress();
	}
	
	/**
	 * Get Local socket address
	 * @return	Local Address
	 */
	public InetAddress getLocalAddress() {
		return socket.getLocalAddress();
	}
	
	/**
	 * Get Remote socket address
	 * @return Local socket address
	 */
	public SocketAddress getRemoteAddress() {
		return socket.getRemoteSocketAddress();
	}
	
	/**
	 * Get Local port
	 * @return local port
	 */
	public int getLocalPort() {
		return socket.getLocalPort();
	}
}
